package org.jflame.commons.util;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.jflame.commons.valid.LazyPattern;

/**
 * 存储单位换算和解析工具类
 * 
 * @author charles.zhang
 */
public class DataSizeHelper {

    private static LazyPattern PATTERN = new LazyPattern("^(\\d+)([k|m|g|t]?b)?$", Pattern.CASE_INSENSITIVE);
    public static final long KILO = 1024L;

    /**
     * 解析带存储单位的数据大小字符串,换算为字节b
     * 
     * @param sizeStr 带存储单位的数据大小字符串,如:20,20b,100kb,100mb
     * @return 字节数
     */
    public static long parse(final String sizeStr) {
        return parse(sizeStr, 0);
    }

    /**
     * 解析带存储单位的数据大小字符串,换算为kb
     * 
     * @param sizeStr sizeStr 带存储单位的数据大小字符串,如:20,20b,100kb,100mb
     * @return kb数
     */
    public static long parseAsKB(final String sizeStr) {
        return parse(sizeStr, 1);
    }

    /**
     * 解析带存储单位的数据大小字符串,换算为mb
     * 
     * @param sizeStr 带存储单位的数据大小字符串,如:20,20b,100kb,100mb
     * @return mb数
     */
    public static long parseAsMB(final String sizeStr) {
        return parse(sizeStr, 2);
    }

    /**
     * 解析带存储单位的数据大小字符串,换算为给定单位大小
     * 
     * @param sizeStr 带存储单位的数据大小字符串,如:20,20b,100kb,100mb,100gb
     * @param unitFlag 单位标识, 0:b,1:kb,2:mb,3:gb,4:tb,5:pb
     * @return
     */
    public static long parse(final String sizeStr, final int unitFlag) {
        StringHelper.requireNonEmpty(sizeStr, "sizeStr must not be empty");
        String[] units = { "b","kb","mb","gb","tb","pb" };
        if (unitFlag < 0 || unitFlag > units.length) {
            throw new IllegalArgumentException("unitFlag must be between 0 and " + units.length);
        }

        Matcher matcher = PATTERN.matcher(sizeStr.trim());
        if (!matcher.matches()) {
            throw new IllegalArgumentException("'" + sizeStr + "' is not a valid data size");
        }

        long amount;
        try {
            amount = Long.parseLong(matcher.group(1));
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException("'" + sizeStr + "' is not a valid data size");
        }
        String unitStr = matcher.group(2);
        if (unitStr == null) {
            unitStr = units[0];
        } else {
            unitStr = unitStr.toLowerCase();
        }
        if (unitStr.equals(units[unitFlag])) {
            return amount;
        }
        for (int i = 0; i < units.length; i++) {
            if (unitStr.equals(units[i])) {
                if (i > unitFlag) {
                    return Math.multiplyExact(amount, (long) Math.pow(KILO, i - unitFlag));
                } else {
                    return amount / (long) Math.pow(KILO, unitFlag - i);
                }
            }
        }
        throw new IllegalArgumentException("Unexpected value: " + unitStr);
    }

    /**
     * 字节转换为KB
     * 
     * @param sizeInByte 字节数
     * @return kb,保留2位小数
     */
    public static double BToKB(final long sizeInByte) {
        return MathHelper.divide(sizeInByte, KILO, 2);
    }

    /**
     * 字节转换为MB
     * 
     * @param sizeInByte 字节数
     * @return
     */
    public static double BToMB(final long sizeInByte) {
        return MathHelper.divide(sizeInByte, 1048576, 2);
    }

    /**
     * 字节转换为GB
     * 
     * @param sizeInKB KB数
     * @return
     */
    public static long KBToB(final double sizeInKB) {
        return (long) (sizeInKB * KILO);
    }

    /**
     * KB转换为MB
     * 
     * @param sizeInKB KB数
     * @return
     */
    public static double KBToMB(final long sizeInKB) {
        return MathHelper.divide(sizeInKB, KILO, 2);
    }

    /**
     * MB转换为B
     * 
     * @param sizeInMB MB数
     * @return
     */
    public static long MBToB(final double sizeInMB) {
        return (long) (sizeInMB * 1048576);
    }

    /**
     * MB转换为KB
     * 
     * @param sizeInMB MB数
     * @return
     */
    public static long MBToKB(final double sizeInMB) {
        return (long) (sizeInMB * KILO);
    }

    public static double MBToTB(final double sizeInMB) {
        return MathHelper.divide(sizeInMB, KILO, 2);
    }

    public static long GBToMB(final double sizeInGB) {
        return (long) (sizeInGB * KILO);
    }
}
